/*
Rami El Mawas
rami.contact@gmail.com
*/
#include "rpn.h"

void vStrcpy( char ** cppDst, const char * cpSrc ) {
  *cppDst = new char[ strlen( cpSrc )+1 ];
  strcpy( *cppDst, cpSrc );
}

Node::Node( char * cpString ): nodepNext( NULL ), cpValue( NULL ), dValue( 0.0 ), iType( NOTYPE ), iOp( NOOP ), iAttrIndex( -1 ), iError( 0 )  {
  vSetType( cpString );
}

Node::~Node() {
  printf( "~Node: " );
  vPrint();
  delete [] cpValue;
  cpValue = NULL;
}

const double Node::dpConstants[] = { 
  3.14159265, // PI
  2.71828183  // E
};

void Node::vPrint() {
  printf( "type: %i, op: %i, sval: <%s>, dval: %f, footp: %i\n", iType, iOp, cpValue, dValue, iFootPrint );
}

double Node::dConstCalc() {
  double total = 0.0;
  if( iOp == PI )
    total = dpConstants[0];
  else if( iOp == E )
    total = dpConstants[1];
  return total;
}

double Node::dArthCalc( double val1, double val2 ) {
  double total = 0.0;
  if( iOp==PLUS )
    total = val1 + val2;
  else if( iOp==MINUS)
    total = val2 - val1;
  else if( iOp==MULTIPLY )
    total = val1 * val2;
  else if( iOp==DIVIDE )
    total = iValidValue( total = val2 / val1 ) ? total : 0.0;
  return total;
}

double Node::dFuncCalc( double val ) {
  double total = 0.0;
  if( iOp == COS ) total = cos(val);
  else if( iOp==SIN ) total = sin(val);
  else if( iOp==TAN ) { 
    total = iValidValue( total = tan(val) ) ? total : 0.0; } 
  else if( iOp==ACOS ) { 
    total = iValidValue( total = acos(val) ) ? total : 0.0; } 
  else if( iOp==ASIN ) { 
    total = iValidValue( total = asin(val) ) ? total : 0.0; }
  else if( iOp==ATAN ) { 
    total = iValidValue( total = atan(val) ) ? total : 0.0; }
  else if( iOp==COSH )
    total = cosh(val);
  else if( iOp==SINH )
    total = sinh(val);
  else if( iOp==TANH )
    total = tanh(val);
  else if( iOp==EXP )
    total = exp(val);
  else if( iOp==LOG ) {
    total = iValidValue( total = log(val) ) ? total : 0.0; }
  else if( iOp==LOG10 ) {
    total = iValidValue( total = log10(val) ) ? total : 0.0; }
  else if( iOp==SQRT ) {
    total = iValidValue( total = sqrt(val) ) ? total : 0.0; }
  else if( iOp==CEIL )
    total = ceil(val);
  else if( iOp==FABS )
    total = fabs(val);
  else if( iOp==FLOOR )
    total = floor(val);
  return total; 
}

void Node::vGetOp( char * op ) {
  if( iType==ARTH || iType==CONST || iType==FUNC ) {
    if( strcmp( op, "+" )==0 ) { iOp = PLUS; iFootPrint = -1; }
    else if( strcmp( op, "-" )==0 ) { iOp = PLUS; iFootPrint = -1; }
    else if( strcmp( op, "*" )==0 ) { iOp = MULTIPLY; iFootPrint = -1; }
    else if( strcmp( op, "/" )==0 ) { iOp = DIVIDE; iFootPrint = -1; }
    else if( strcmp( op, "cos" )==0 ) { iOp = COS; iFootPrint = 0; }
    else if( strcmp( op, "sin" )==0 ) { iOp = SIN; iFootPrint = 0; }
    else if( strcmp( op, "tan" )==0 ) { iOp = TAN; iFootPrint = 0; }
    else if( strcmp( op, "asin" )==0 ) { iOp = ASIN; iFootPrint = 0; }
    else if( strcmp( op, "acos" )==0 ) { iOp = ACOS; iFootPrint = 0; }
    else if( strcmp( op, "atan" )==0 ) { iOp = ATAN; iFootPrint = 0; }
    else if( strcmp( op, "cosh" )==0 ) { iOp = COSH; iFootPrint = 0; }
    else if( strcmp( op, "sinh" )==0 ) { iOp = SINH; iFootPrint = 0; }
    else if( strcmp( op, "tanh" )==0 ) { iOp = TANH; iFootPrint = 0; }
    else if( strcmp( op, "exp" )==0 ) { iOp = EXP; iFootPrint = 0; }
    else if( strcmp( op, "log" )==0 ) { iOp = LOG; iFootPrint = 0; }
    else if( strcmp( op, "log10" )==0 ) { iOp = LOG10; iFootPrint = 0; }
    else if( strcmp( op, "sqrt" )==0 ) { iOp = SQRT; iFootPrint = 0; }
    else if( strcmp( op, "ceil" )==0 ) { iOp = CEIL; iFootPrint = 0; }
    else if( strcmp( op, "fabs" )==0 ) { iOp = FABS; iFootPrint = 0; }
    else if( strcmp( op, "floor" )==0 ) { iOp = FLOOR; iFootPrint = 0; }
    else if( strcmp( op, "PI" )==0 ) { iOp = PI; iFootPrint = 1; }
    else {
      if( iType==ARTH )
        iError = 2;
      else if( iType==CONST )
        iError = 3;
      else if( iType==FUNC )
        iError = 4;
    }
  } else if( iType==NUM || iType==ATTR )
    iFootPrint = 1;
}

// private methods
int Node::iValidValue( double total ) {
  int iFlag = 1;
  if( isnan( total ) || isinf( total ) ) {
    iError = 7;
    iFlag = 0;
  }
  return iFlag;
}

void Node::vSetType( char * cpString ) {
  char cFirst = (char) *cpString;
  switch( cFirst ) {
    case '#':
      iType = CONST;
      break;
    case '+':
    case '-':
      if( strlen( cpString )==1 )
        iType = ARTH;
      break;
    case '*':
    case '/':
      iType = ARTH;
      break;
    case '@':
      iType = ATTR;
      break;
    case '$':
      iType = FUNC;
      break;
  }
  if( iType==NOTYPE ) {
    char * cpEnd;
    if( dValue = strtod( cpString, &cpEnd ), cpEnd != cpString && *cpEnd == '\0' )
      iType = NUM;
    else
      iError = 1;
  } else if( iType==CONST || iType==FUNC || iType==ATTR )
    cpString += 1;
  vGetOp( cpString );
  vStrcpy( &cpValue, cpString );
  if( iType==ATTR) { // pre-calculate attribute index and constants.
    iAttrIndex =  atoi( cpValue );
    if( iAttrIndex==-1 )
      iError = 5;
  } else if( iType==CONST )
    dValue = dConstCalc();
}

/*
template <class T>
Stack<T>::Stack( const char * cpEquationNew, T* tpAtrributes ): iError( 0 ), nodepStack( NULL ), iSize( 0), iSyntaxCheck( 0 ) {
  char * cpEquationTemp = NULL, * cpEq;
  const char * cpTok = " ";
  vStrcpy( &cpEquation, cpEquationNew );
  vStrcpy( &cpEquationTemp, cpEquationNew );
  cpEq = strtok( cpEquationTemp, cpTok );
  while ( cpEq != NULL ) {
    vAdd( cpEq );
    cpEq = strtok( NULL, cpTok );
  }
  delete [] cpEquationTemp;
  if( !iError && iSyntaxCheck != 1 )
    iError = 6;
}

template <class T>
Stack<T>::~Stack() {
  printf( "~Stack: <%s>\n", cpEquation );
  delete [] cpEquation;
  Node * t1 = nodepStack, * t2;
  while( t1 != NULL ) {
    t2 = t1->nodepNext;
    delete t1;
    t1 = t2;
  }
  fflush( stdout );
}
*/

template <class T>
void Stack<T>::vPrint() {
  Node * temp = nodepStack;
  int i = 0;
  printf( ">> printing stack\n\tequation: <%s>\n", cpEquation );
  while( temp != NULL) {
    printf( "\tNode %i: " , i );
    temp->vPrint();
    temp = temp->nodepNext;
    i++;
  }
  printf( "<<\n" );
}

/*
template <class T>
double Stack<T>::dEvaluate() {
  double total = 0.0;
  if( nodepStack != NULL ) {
    temp = nodepStack;
    total = eval();
  } 
  return total;
}
*/

template <class T>
void Stack<T>::vDispatchError( char **cpError ) {
  int iErrIndex = 0;
  switch( iError ) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
      iErrIndex = iError;
      break;
  }
  *cpError = new char[ strlen( cppErrors[iErrIndex] ) + 1 ];
  strcpy( *cpError, cppErrors[iErrIndex] );
}
/*
template <class T>
void Stack<T>::vAdd( char * string ) {
  Node *nodeNew = new Node( string );
  if( nodeNew->iError ) {
    iError = nodeNew->iError;
  }
  iSyntaxCheck += nodeNew->iFootPrint;
  if( iSize == 0 )
    nodepStack = nodeNew;
  else {
    nodeNew->nodepNext = nodepStack;
    nodepStack = nodeNew;
  }
  iSize++;
}

template <class T>
double Stack<T>::eval() {
  Node * current = temp;
  double total = 0.0;
  int iType = current->iType;
  switch( iType ) {
    case NUM:
    case CONST:
      total = current->dValue;
      break;
    case ARTH:
      {
      temp = temp->nodepNext;
      double val1 = eval();
      temp = temp->nodepNext;
      double val2 = eval();
      total = current->dArthCalc( val1, val2 );
      }
      break;
    case FUNC:
      {
        temp = temp->nodepNext;
        double val = eval();
        total = current->dFuncCalc( val );
      }
      break;
    case ATTR:
      total = 10;
      break;
  }
  if( current->iError )
    iError = current->iError;
  return current->dValue = total;
}
*/
///*
// private methods
template <class T>
const char * Stack<T>::cppErrors[] = {
  "Sorter: Unknown error",                                  // 0
  "Sorter: Node Error. [1] Invalid equation term",          // 1
  "Sorter: Node Error. [2] Arithmatic operation not found", // 2
  "Sorter: Node Error. [3] Constant not found",             // 3
  "Sorter: Node Error. [4] Function not found",             // 4
  "Sorter: Node Error. [5] Invalide sphinx attribute",      // 5
  "Sorter: Stack Error. [6] Safety check failed",           // 6
  "Sorter: Stack Error. [7] Evaluation failed"              // 7
  };
//*/
/* end of RPN */

